package com.base.cn.platform.os.controller.manage.login;

import com.base.cn.platform.os.common.constants.CacheConstants;
import com.base.cn.platform.os.common.constants.CommonConstants;
import com.base.cn.platform.os.common.constants.ConfigName;
import com.base.cn.platform.os.common.controller.BaseController;
import com.base.cn.platform.os.common.j2cache.CustomCacheChannel;
import com.base.cn.platform.os.common.j2cache.J2CacheRegion;
import com.base.cn.platform.os.common.log.aop.AdminModuleEnum;
import com.base.cn.platform.os.common.log.aop.LogUtils;
import com.base.cn.platform.os.common.log.aop.OpLog;
import com.base.cn.platform.os.common.md5.MD5;
import com.base.cn.platform.os.common.utils.DateUtils;
import com.base.cn.platform.os.common.utils.ObjectUtils;
import com.base.cn.platform.os.common.utils.StringUtils;
import com.base.cn.platform.os.common.utils.result.ResultUtil;
import com.base.cn.platform.os.common.utils.sms.SmsSendUtils;
import com.base.cn.platform.os.common.utils.web.WebUtils;
import com.base.cn.platform.os.entity.manage.user.SysUser;
import com.base.cn.platform.os.entity.manage.user.SysUserForm;
import com.base.cn.platform.os.service.manage.config.SysConfigBiz;
import com.base.cn.platform.os.service.manage.login.SysLoginLogBiz;
import com.base.cn.platform.os.service.manage.user.SysUserBiz;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.aop.framework.AopContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.IntStream;

/**
 * 登录接口Controller
 *
 * @author s.li
 * @create 2018-03-13 9:28
 */
@RestController
@RequestMapping("/manage")
@Slf4j
public class SysUserLoginController extends BaseController {
    private Gson gson = new GsonBuilder().setDateFormat("yyyy-MM-dd HH:mm:ss").create();
    @Autowired
    private CustomCacheChannel customCacheChannel;
    @Autowired
    private SysUserBiz sysUserBiz;
    @Autowired
    private SysLoginLogBiz sysLoginLogBiz;
    @Autowired
    private SmsSendUtils smsSendUtils;
    @Autowired
    private SysConfigBiz sysConfigBiz;

    /**
     * 执行退出登录
     * @param sessionId 会话ID
     * @param userId 用户ID
     * @return true退出成功，false退出失败
     */
    @RequestMapping("/executeLogout")
    public boolean executeLogout(String sessionId,BigDecimal userId){
        try{
            //获取要退出登录的用户的所有缓存的会话
            List<Map<String,Object>> sessionList = (List<Map<String,Object>>)customCacheChannel.get(J2CacheRegion.ONE_DAY_REGION.getKey(),CacheConstants.LOGIN_USER_SESSION_KEY_PREFIX+userId.toString()).getValue();
            if(sessionList==null){
                sessionList = new ArrayList<>();
            }
            //删除缓存中的Session数据
            sessionList = this.deleteLogoutSession(sessionId,sessionList);
            //删除用户信息缓存
            customCacheChannel.evict(J2CacheRegion.ONE_DAY_REGION.getKey(),sessionId);
            if(sessionList.size()>0){
                //重新缓存用户的会话
                customCacheChannel.set(J2CacheRegion.ONE_DAY_REGION.getKey(),CacheConstants.LOGIN_USER_SESSION_KEY_PREFIX+userId.toString(),sessionList);
            }else{
                //如果用户的所有会话已经注销完了，则直接删除缓存
                customCacheChannel.evict(J2CacheRegion.ONE_DAY_REGION.getKey(),CacheConstants.LOGIN_USER_SESSION_KEY_PREFIX+userId.toString());
            }
            //删除用户权限缓存
            customCacheChannel.evict(J2CacheRegion.ONE_DAY_REGION.getKey(),CacheConstants.USER_ALL_FUNCTION_LIST_KEY_PREFIX+userId.toString());
            String key = sessionId + CacheConstants.LOGIN_USER_SUBJECT_KEY_SUFFIX;
            //删除用户登录缓存的专业数据
            customCacheChannel.evict(J2CacheRegion.ONE_DAY_REGION.getKey(),key);
        }catch (Exception e){
            logger.error("---------------manage os user logout error--------------");
            return false;
        }
        return true;
    }

    /**
     * 删除退出Session
     * @param sessionId
     * @param sessionList
     * @return List<Map<String,Object>>
     */
    private List<Map<String,Object>> deleteLogoutSession(String sessionId, List<Map<String, Object>> sessionList) {
        List<Map<String,Object>> _sessionList = new ArrayList<Map<String,Object>>();
        if(StringUtils.isNotEmpty(sessionId) && ObjectUtils.isNotEmpty(sessionList)){
            for(Map<String,Object> session : sessionList){
                String _sessionId = (String)session.get("sessionId");
                if(!_sessionId.equals(sessionId)){
                    _sessionList.add(session);
                }
            }
        }
        return _sessionList;
    }

    /**
     * 执行注册管理员接口
     * @param sysUserForm 管理员数据对象
     * @return Map<String,Object>
     */
    @RequestMapping("/executeRegister")
    public Map<String,Object> executeRegister(@RequestBody SysUserForm sysUserForm){
        String message = "";
        if(sysUserForm.getRegType()==2){//体验账号申请
            if(StringUtils.isEmpty(sysUserForm.getAccount())){
                return ResultUtil.ERROR("请填写手机号");
            }
            if(!StringUtils.isMobile(sysUserForm.getAccount())){
                return ResultUtil.ERROR("请填写正确的手机号");
            }
            //手机验证码------------------开始---------
            TreeMap<String,String> configMap = sysConfigBiz.getSysConfigContext(ConfigName.SMS);
            smsSendUtils.setSmsConfig(configMap);
            boolean isOk = smsSendUtils.validateTestUserCode(sysUserForm.getAccount(),sysUserForm.getCaptcha());
            if(!isOk){
                return ResultUtil.ERROR("手机验证码错误");
            }
            //手机验证码------------------结束---------
            if(StringUtils.isEmpty(sysUserForm.getPassword())){
                return ResultUtil.ERROR("请填写登录密码");
            }
            if(!sysUserForm.getPassword().trim().equals(sysUserForm.getConfirmPwd())){
                return ResultUtil.ERROR("两次输入密码不一致");
            }
            if(!StringUtils.isLetterOrNum(sysUserForm.getPassword())
                    || sysUserForm.getPassword().trim().length()<6
                    || sysUserForm.getPassword().trim().length()>20){
                return ResultUtil.ERROR("密码必须是数字或英文，且大6位小于20位");
            }
            SysUser _sysUser = sysUserBiz.findSysUserByUsername(sysUserForm.getAccount());
            if(ObjectUtils.isNotEmpty(_sysUser)){
                return ResultUtil.ERROR("该手机号已申请过");
            }
            message = "体验账号申请成功，客服审核中";
        }else if(sysUserForm.getRegType()==1){//后台开通
            if(StringUtils.isEmpty(sysUserForm.getAccount())){
                return ResultUtil.ERROR("请填写登录账号");
            }
            if(StringUtils.isLetterOrNum(sysUserForm.getAccount())
                    || sysUserForm.getAccount().trim().length()<6
                    || sysUserForm.getAccount().trim().length()>30){
                return ResultUtil.ERROR("登录账号必须是数字或英文，且大6位小于30位");
            }
            if(StringUtils.isEmpty(sysUserForm.getRalName())){
                return ResultUtil.ERROR("请填写真实姓名");
            }
            if(StringUtils.isEmpty(sysUserForm.getMobile()) || !StringUtils.isMobile(sysUserForm.getMobile())){
                return ResultUtil.ERROR("请填写正确的手机号");
            }
            if(sysUserForm.getUserType()==1){//正式账号
                if(StringUtils.isEmpty(sysUserForm.getGroupIds())){
                    return ResultUtil.ERROR("请选择账号所属部门");
                }
                if(StringUtils.isEmpty(sysUserForm.getRoleIds())){
                    return ResultUtil.ERROR("请选择账号所属的角色");
                }
            }
            //如果同步前台用户，则进行判断
            if(StringUtils.isNotEmpty(sysUserForm.getIsSync()) && sysUserForm.getIsSync().toUpperCase().equals("ON")){
                logger.info("--------------------进入同步前台用户判断");
            }
            //如果同步讲师，则进行判断
            if(StringUtils.isNotEmpty(sysUserForm.getIsTeacher()) && sysUserForm.getIsTeacher().toUpperCase().equals("ON")){
                logger.info("--------------------进入同步讲师判断");
            }
            SysUser _sysUser = sysUserBiz.findSysUserByUsername(sysUserForm.getAccount(),sysUserForm.getId());
            if(ObjectUtils.isNotEmpty(_sysUser)){
                return ResultUtil.ERROR("账号名称已存在");
            }
            _sysUser = sysUserBiz.findSysUserByMobile(sysUserForm.getMobile(),sysUserForm.getId());
            if(ObjectUtils.isNotEmpty(_sysUser)){
                return ResultUtil.ERROR("该手机号已被使用");
            }
            message="用户信息保存成功";
        }
        //数据对象转换
        SysUser sysUser = sysUserBiz.formToSysUser(sysUserForm);
        //执行创建
        sysUserBiz.createSysUser(sysUser,sysUserForm);
        return ResultUtil.SUCCESS(message,sysUser);
    }

    /**
     * 执行登录接口
     * @param sysUser 登录的用户信息
     * @param gapLeft 图片的原坐标
     * @param moveLeft 移动的图片的坐标
     * @return Map<String,Object>
     */
    @RequestMapping("/executeLogin")
    public Map<String,Object> executeLogin(HttpServletRequest request,
                                           @RequestBody SysUser sysUser,
                                           @RequestParam(value = "gapLeft",required = false,defaultValue = "0") int gapLeft,
                                           @RequestParam(value = "moveLeft",required = false,defaultValue = "0") int moveLeft){
        if(StringUtils.isEmpty(sysUser.getUsername())){
            return ResultUtil.ERROR("请输入账号名/手机");
        }
        if(StringUtils.isEmpty(sysUser.getPassword())){
            return ResultUtil.ERROR("输入密码");
        }
        if(!this.validateCode(gapLeft,moveLeft)){
            return ResultUtil.ERROR("验证码错误");
        }
        SysUser _sysUser = sysUserBiz.findSysUserByUsername(sysUser.getUsername());
        if(ObjectUtils.isEmpty(_sysUser)){
            return ResultUtil.ERROR("用户不存在");
        }
        if(!MD5.getMD5(sysUser.getPassword()).equals(_sysUser.getPassword())){
            return ResultUtil.ERROR("登录密码错误");
        }
        if(_sysUser.getStatus()==0){
            return ResultUtil.ERROR("该账号未授权");
        }
        if(_sysUser.getStatus()==2){
            return ResultUtil.ERROR("该账号已被冻结");
        }
        if(_sysUser.getStatus()!=1){
            return ResultUtil.ERROR("账号状态异常");
        }
        if (_sysUser.getUserType() == 2){
            if (_sysUser.getStatus() == 0){
                return ResultUtil.ERROR("该体验账号暂未通过申请");
            }
            if (_sysUser.getStatus() == 1 && _sysUser.getEndTime() != null && new Date().after(_sysUser.getEndTime())){
                return ResultUtil.ERROR("该体验账号已过期");
            }
            if (_sysUser.getStatus() == 2){
                return ResultUtil.ERROR("账号已被禁用");
            }
        }
        Map<String,Object> resultMap = ((SysUserLoginController)AopContext.currentProxy()).gotoLogin(request,_sysUser);
        //如果原来有登录过，则退出原来的用户
        String sin = WebUtils.getCookie(request,CommonConstants.ADMIN_LOGIN_COOKIE);
        this.executeLogout(sin,_sysUser.getId());
        return ResultUtil.to(resultMap,null);
    }

    /**
     * 修改当前登录用户的密码
     * @param userId 用户ID
     * @param oldPassword 原密码
     * @param newPassword 新密码
     * @param confirmPassword 确认密码
     * @return Map<String,Object>
     */
    @RequestMapping("/updateThisPwd")
    public Map<String,Object> updateThisPwd(@RequestParam("userId") BigDecimal userId,
                                     @RequestParam("oldPassword") String oldPassword,
                                     @RequestParam("newPassword") String newPassword,
                                     @RequestParam("confirmPassword") String confirmPassword){
        SysUser sysUser = sysUserBiz.findSysUserById(userId,false,false,false);
        if(ObjectUtils.isEmpty(sysUser)){
            return ResultUtil.ERROR("用户不存在");
        }
        if(!sysUser.getPassword().equals(MD5.getMD5(oldPassword))){
            return ResultUtil.ERROR("原密码不正确");
        }
        if(!newPassword.trim().equals(confirmPassword)){
            return ResultUtil.ERROR("两次输入密码不一致");
        }
        if(sysUser.getPassword().equals(MD5.getMD5(newPassword))){
            return ResultUtil.ERROR("新密码和原密码不可相同");
        }
        if(!StringUtils.isLetterOrNum(newPassword)
                || newPassword.trim().length()<6
                || newPassword.trim().length()>20){
            return ResultUtil.ERROR("新密码必须是数字或英文，且大6位小于20位");
        }
        sysUserBiz.updateSysUserPwd(userId,newPassword);
        return ResultUtil.SUCCESS("密码修改成功");
    }

    //===========================================

    /**
     * 进行登录登录设置
     * @param request HttpServletRequest
     * @param sysUser 要登录的用户对象
     */
    @OpLog(module = AdminModuleEnum.SYS_USER,action = "系统用户登录")
    public Map<String,Object> gotoLogin(HttpServletRequest request,SysUser sysUser){
        Map<String,Object> resultMap = new HashMap<>();
        try {
            Integer userType = sysUser.getUserType();
            if (userType == 2){
                BigDecimal count = sysLoginLogBiz.count(" userId = " + sysUser.getId());
                if (count.intValue() <= 0){
                    sysUser.setEndTime(DateUtils.getDateAfterDays(new Date(), 7));
                    sysUserBiz.updateByWhereSql(sysUser," id ="+sysUser.getId());
                }
            }
            LogUtils.get().setAttr("account",sysUser.getUsername());
            resultMap.put("success",true);
            int loginNum = 10;//一个用户最大可以存在多个登录Session
            boolean disable_login = false;//超过最大Session量，是否禁止登录，false否-踢出之前登录，true是-不可再登录
            BigDecimal userId = sysUser.getId();
            //用户所有的登录session的Map集合
            List<Map<String,Object>> sessionList = (List<Map<String,Object>>)customCacheChannel.get(J2CacheRegion.ONE_DAY_REGION.getKey(),CacheConstants.LOGIN_USER_SESSION_KEY_PREFIX+userId.toString()).getValue();
            if(sessionList==null){
                sessionList = new ArrayList<>();
            }
            //----------------------------------------------
            //如果已登录的Session量大于限制量
            if(sessionList.size() >= loginNum){
                if(disable_login){//禁止登录
                    resultMap.put("status","1");//禁止登录
                    resultMap.put("success",false);
                    LogUtils.get().setAttr("action","登录失败，登录用户限制为10人");
                    return resultMap;
                }else{//踢出之前登录的用户
                    sessionList = this.deleteSession(sessionList,loginNum);
                    LogUtils.get().setAttr("action","登录成功，本账号之前登录的用户被踢出");
                }
            }
            //----------------------------------------------
            //每次登录成功都生成一个SessionID
            String sessionId =MD5.getMD5(System.currentTimeMillis()+StringUtils.getRandStr(32)+userId);
            Map<String,Object> session = new HashMap<String,Object>();
            //Session登录时间
            session.put("loginTime", new Date());
            session.put("sessionId", sessionId);
            session.put("userId", userId);
            sessionList.add(session);
            //---------------添加登录日志，开始--------------------
            sysLoginLogBiz.saveSysLoginLog(request,sysUser);
            //---------------添加登录日志，结束--------------------
            customCacheChannel.set(J2CacheRegion.ONE_DAY_REGION.getKey(),CacheConstants.LOGIN_USER_SESSION_KEY_PREFIX+userId.toString(),sessionList);
            customCacheChannel.set(J2CacheRegion.ONE_DAY_REGION.getKey(),sessionId,gson.toJson(sysUser));
            resultMap.put("sessionId",sessionId);
            resultMap.put("status","0");//状态为0时，登录成功
            return resultMap;
        }catch (Exception e){
            log.error("----登录异常:",e);
            return ResultUtil.ERROR("登录异常");
        }
    }

    //删除之前登录的Session，用于限制登录
    private  List<Map<String,Object>> deleteSession(List<Map<String,Object>> sessionList,int loginNum) {
        List<Map<String,Object>> _sessionList = new ArrayList<Map<String,Object>>();
        if(ObjectUtils.isNotEmpty(sessionList) && sessionList.size() >= loginNum){
            //重新排序
            Collections.sort(sessionList,(x, y) -> {
                    Date xDate = (Date) x.get("loginTime");
                    Date yDate = (Date) y.get("loginTime");
                    if(xDate.after(yDate)){
                        return 1;
                    }else if(xDate.before(yDate)){
                        return -1;
                    }
                    return 0;
            });
            List<String> delKeyList =new ArrayList<String>();
            IntStream.range(0,sessionList.size()).forEach(index -> {
                Map<String,Object> session = sessionList.get(index);
                if(index < loginNum-1){
                    _sessionList.add(session);
                }else{
                    delKeyList.add((String)session.get("sessionId"));
                }
            });
            List<Map<String,Object>> _list = new ArrayList<>();
            //对于剩余的sessionKey则需要判断是否过期
            if(ObjectUtils.isNotEmpty(_sessionList)){
                _sessionList.forEach(session->{
                    boolean e = customCacheChannel.exists(J2CacheRegion.ONE_DAY_REGION.getKey(), (String)session.get("sessionId"));
                    if(e){
                        _list.add(session);
                    }
                });
            }
            if(ObjectUtils.isNotEmpty(delKeyList)){//删除被顶掉的用户缓存
                customCacheChannel.evict(J2CacheRegion.ONE_DAY_REGION.getKey(),delKeyList.toArray(new String[]{}));
            }
            return _list;
        }
        return _sessionList;
    }

    /**
     * 验证图片验证码
     * @param gapLeft 固定图片坐标
     * @param moveLeft 移动图片坐标
     * @return false验证不通过，true验证通过
     */
    private boolean validateCode(int gapLeft,int moveLeft){
        int dvalue = 5;
        int v = Math.abs(gapLeft - moveLeft);
        if(gapLeft==0 || moveLeft==0 || v > dvalue){
            return false;
        }
        return true;
    }

    @RequestMapping("/sendCaptcha")
    public Map<String,Object> sendCaptcha  (@RequestParam("mobile") String mobile) throws Exception {
        boolean sendStatus = true;
        Map<String,Object> resultMap = null;
        TreeMap<String,String> configMap = sysConfigBiz.getSysConfigContext(ConfigName.SMS);
        smsSendUtils.setSmsConfig(configMap);
        //查询手机号是否被使用
        SysUser sysUser = sysUserBiz.findSysUserByMobile(mobile);
        if(ObjectUtils.isNotEmpty(sysUser)){
            return ResultUtil.ERROR("该手机号已被使用");
        }else{
            sendStatus = smsSendUtils.sendTestUserCode(mobile.trim());
        }

        if(sendStatus){
            resultMap = ResultUtil.SUCCESS("短信发送成功");
        }else{
            resultMap = ResultUtil.ERROR("频繁操作，短信发送失败");
        }
        return  resultMap;
    }
}
